package com.aceitunaproject.client.mapscreen;

import com.aceitunaproject.clientinterface.*;
import com.aceitunaproject.clientinterface.eventadapters.*;
import com.aceitunaproject.clientinterface.eventregistering.*;
import com.aceitunaproject.mapdisplay.*;
import com.aceitunaproject.mapdisplay.model.Thing;
import com.aceitunaproject.mapdisplay.model.map.Map;
import com.aceitunaproject.mapdisplay.model.map.Position;
import com.aceitunaproject.mapdisplay.model.map.tile.Tile;
import com.aceitunaproject.shared.gamelement.Unit;
import com.aceitunaproject.shared.map.MapTile;
import com.aceitunaproject.shared.message.concrete.*;

//graphics imports
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import java.util.List;

import javax.swing.DefaultListModel;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * Aceituna Client Main Class
 * 
 * @author Federico Bergstein
 * 
 */
public class AceitunaClient extends JFrame implements ActionListener,
		AceitunaAllEventsHandler, AceitunaConnectionEventsHandler,
		MouseListener, ListSelectionListener {

	/**
	 * Obligatory serial version id...
	 */
	private static final long serialVersionUID = 1L;

	/** The input field for the user to enter a chat message. */
	protected final JTextField inputField;

	/** The panel that wraps the text input field */
	protected final JPanel inputPanel;

	/** the panel that wraps the terrain and unit data. */
	protected final JPanel detailsPanel;

	/** The status indicator. */
	protected final JLabel statusLabel;

	/** the map panel */
	protected MapPane mapPanel = new MapPane();

	/** the jlist that displays a list of units */
	protected JList list;

	/** the data model for the unit list */
	protected DefaultListModel listModel;

	/** the ids of the listed units **/
	protected Long[] listedUnits;

	/** the label with the terrain info */
	protected JLabel terrainLabel;

	/** the last received map (the map that is currently on screen) */
	protected List<MapTile> lastMap;

	/** id of the selected unit */
	protected Long selectedUnitID;

	/** the interface with the aceituna server */
	protected AceitunaClientInterface clientInterface;

	/** the map drawer class to display the map graphics */
	protected MapDrawer mapDrawer;

	/** the logged in flag */
	protected boolean isLoggedIn = false;

	@Override
	public void ChatMessageReceived(ChatMessageReceivedEvent message) {
		// TODO Auto-generated method stub

	}

	@Override
	public void MapAreaRefresh(MapAreaRefreshEvent message) {

		drawMap(message);
		lastMap = message.getAreaTiles();
	}

	/**
	 * Runs an instance of this client.
	 * 
	 * @param args
	 *            the command-line arguments (unused)
	 */
	public static void main(String[] args) {
		new AceitunaClient();
	}

	/**
	 * Creates a new client UI.
	 */
	public AceitunaClient() {
		this("Aceituna Client");
	}

	/**
	 * Creates a new client UI with the given window title.
	 * 
	 * @param title
	 *            the title for the client's window
	 */
	protected AceitunaClient(String title) {
		super(title);

		// set up
		Container c = getContentPane();
		c.setLayout(new BorderLayout());

		// Unit list setup
		listModel = new DefaultListModel();
		list = new JList(listModel);
		list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		list.setSelectedIndex(0);
		list.addListSelectionListener(this);
		list.setVisibleRowCount(5);

		// initializes the terrain label
		terrainLabel = new JLabel();

		// setup the details panel
		detailsPanel = new JPanel();
		detailsPanel.setLayout(new BorderLayout());
		detailsPanel.add(terrainLabel, BorderLayout.NORTH);
		detailsPanel.add(list, BorderLayout.CENTER);

		// setup the input field & panel
		inputField = new JTextField();
		inputField.addActionListener(this);
		inputPanel = new JPanel();
		inputPanel.setLayout(new BorderLayout());
		inputPanel.add(inputField, BorderLayout.SOUTH);
		inputPanel.setEnabled(false);

		// set up the status label
		statusLabel = new JLabel();
		statusLabel.setFocusable(false);
		setStatus("Not Started");

		// sets itself as the mouse listener for the map
		mapPanel.addMouseListener(this);

		// adds panels to container
		c.add(detailsPanel, BorderLayout.EAST);
		c.add(mapPanel, BorderLayout.CENTER);
		c.add(statusLabel, BorderLayout.NORTH);
		c.add(inputPanel, BorderLayout.SOUTH);

		// set up the window properties
		setSize(1024, 768);
		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		setVisible(true);

	}

	/**
	 * Initiates asynchronous login to the SGS server specified by the host and
	 * port properties.
	 */
	protected void login(String userName) {
		clientInterface = new AceitunaClientInterfaceImpl(userName);
		clientInterface.registerToEvent((MapAreaRefreshHandler) this);
		clientInterface.registerToEvent((ChatMessageReceivedHandler) this);
		clientInterface
				.registerToEvent((RuleViolationEventReceivedHandler) this);
		clientInterface.registerForConnectionEvents(this);
		clientInterface.login();

	}

	/**
	 * Displays the given string in this client's status bar.
	 * 
	 * @param status
	 *            the status message to set
	 */
	protected void setStatus(String status) {
		// appendOutput("Status Set: " + status);
		statusLabel.setText("Status: " + status);
	}

	/**
	 * Returns the user-supplied text from the input field, and clears the field
	 * to prepare for more input.
	 * 
	 * @return the user-supplied text from the input field
	 */
	protected String getInputText() {
		try {
			return inputField.getText();
		} finally {
			inputField.setText("");
		}
	}

	public void loggedIn() {
		inputPanel.setEnabled(true);
		setStatus("Logged in");
		this.setTitle("Aceituna Client - Logged In as : "
				+ clientInterface.getPlayer());
		requestMapRefresh();
	}

	public void loginFailed(String reason) {
		setStatus("Login failed: " + reason);
	}

	public void disconnected(boolean graceful, String reason) {
		inputPanel.setEnabled(false);
		setStatus("Disconnected: " + reason);
	}

	public void reconnected() {
		setStatus("reconnected");
	}

	public void reconnecting() {
		setStatus("reconnecting");
	}

	// Implement ActionListener

	/**
	 * {@inheritDoc}
	 * <p>
	 * Parses the user request and sends the appropriate order to the server
	 */
	public void actionPerformed(ActionEvent event) {

		String text = getInputText();

		if (text.contains("login")) {
			System.out.println("login in");
			login(text.substring(6, text.length()));
		} else {
			if (text.contains("move")) {
				Long id = Long.valueOf(text.substring(5, 7));
				int newX = Integer.valueOf(text.substring(8, 9));
				int newY = Integer.valueOf(text.substring(10, 11));

				// setStatus("id=" + id + " x= " + newX + " y= " + newY);

				clientInterface.moveUnit(id, newX, newY);
				requestMapRefresh();// asks for a map refresh
			}

			else {
				if (text.contains("refresh")) {
					requestMapRefresh();// asks for a map refresh;
				} else {
					setStatus("unrecognized command");
				}
			}
		}

	}

	/**
	 * requests a map refresh from the server.
	 */
	private void requestMapRefresh() {

		// Since immediate refresh after login caused exceptions in slow
		// machines/connections.
		// if its not logged in waits a few seconds to let the server finish the
		// login
		if (!isLoggedIn) {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		// sends the message
		clientInterface.requestMapRefresh();

	}

	/**
	 * uses the map drawer to show the map sent by the server.
	 * 
	 * @param message
	 *            the map area refresh event message received.
	 */
	public void drawMap(MapAreaRefreshEvent message) {

		mapDrawer = new MapDrawerImpl(mapPanel, translateMap(message),
				new Dimension(100, 100));
		mapPanel.setMapDrawer(mapDrawer);
		mapDrawer.setFocus(0, 0);
		mapDrawer.setDisplayGrid(false);
		mapPanel.repaint();

	}

	private Map translateMap(MapAreaRefreshEvent message) {

		List<MapTile> receivedTiles = message.getAreaTiles();
		int n = 0;



		Tile[][] tile = new Tile[7][15];
		for (int i = 0; i < tile.length; i++) {
			for (int j = 0; j < tile[i].length; j++) {
				tile[i][j] = createTile(i, j, "");

				n++;
			}
		}

		for (MapTile mapTile : receivedTiles) {

			tile[mapTile.getPosx()][mapTile.getPosy()].setBasicTileType(mapTile.getTerrain().get(0).getName());
			// for (int i = 0: i : mapTile.getUnits()) {
			for (int i = 0; i < mapTile.getUnits().size(); i++) {

				Thing t = new Thing("veteranSoldier");
				tile[mapTile.getPosx()][mapTile.getPosy()].addThing(t);
			}

		}

		Map translatedMap = new Map(tile);
		return translatedMap;

	}

	private Tile createTile(int x, int y, String name) {

		return new Tile(name, x, y);

	}

	@Override
	public void RuleViolationEventReceived(RuleViolationEvent message) {
		setStatus(message.getRuleViolations().getReasons().get(0).toString());
	}

	@Override
	public void mouseClicked(MouseEvent arg0) {

		Position p = mapDrawer
				.convertToMapCoordinates(arg0.getX(), arg0.getY());
		terrainLabel.setText("");
		MapTile theTile = getTile(p.x, p.y);
		List<Unit> theUnits = theTile.getUnits();

		listModel = new DefaultListModel();

		if (arg0.getButton() != MouseEvent.BUTTON1) {

			if (selectedUnitID != null) {
				clientInterface.moveUnit(selectedUnitID, p.x, p.y);
				clientInterface.requestMapRefresh();
				setStatus("moved unit");
			}
		} else {
			mapDrawer.highlightTile(theTile.getPosx(), theTile.getPosy());
			mapPanel.repaint();
			terrainLabel.setText("Tile " + p.x + "," + p.y
					+ ". Terrain Type = " + theTile.getTerrain().get(0).getName());
			listedUnits = new Long[theUnits.size()];
			int i = 0;
			for (Unit unit : theUnits) {
				listedUnits[i] = unit.getId();
				listModel.addElement("Player: " + unit.getPlayer().getName()
						+ ", Unit: " + unit.getName() + ", "
						+ unit.getTimeUnits() + " Time Units");
				i++;
			}

		}
		list.setModel(listModel);
	}

	private MapTile getTile(int x, int y) {
		for (MapTile tile : lastMap) {
			if (tile.getPosx() == x && tile.getPosy() == y) {
				return tile;
			}
		}
		return null;
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void valueChanged(ListSelectionEvent arg0) {
		if (listedUnits != null && listedUnits.length > 0) {
			setStatus("Unit Selected ID = "
					+ listedUnits[list.getSelectedIndex()]);
			selectedUnitID = listedUnits[list.getSelectedIndex()];
		}
	}
}
